package org.feng.controller;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;
import org.feng.config.MySqlDynamicDatasourceConfig;
import org.feng.entity.FormPeople;
import org.feng.entity.Student;
import org.feng.repository.R2dbcEntityTemplateRepository;
import org.feng.repository.StudentRepository;
import org.feng.service.StudentService;
import org.springframework.data.r2dbc.core.R2dbcEntityTemplate;
import org.springframework.data.relational.core.mapping.Column;
import org.springframework.data.relational.core.mapping.Table;
import org.springframework.data.relational.core.query.Criteria;
import org.springframework.data.relational.core.query.Query;
import org.springframework.data.relational.core.query.Update;
import org.springframework.data.relational.core.sql.SqlIdentifier;
import org.springframework.http.MediaType;
import org.springframework.r2dbc.core.DatabaseClient;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.time.Instant;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;

/**
 * 学生控制器
 *
 * @version v1.0
 * @author: fengjinsong
 * @date: 2022年06月11日 11时14分
 */
@Api(tags = "测试控制器")
@Slf4j
@RestController
@RequestMapping("/student")
public class StudentController {

    @Resource
    private StudentService studentService;
    @Resource
    private StudentRepository studentRepository;
    @Resource
    private R2dbcEntityTemplate template;
    @Resource
    private DatabaseClient databaseClient;

    @ApiOperation(value = "测试控制器-测试方法，通过ID获取学生数据", notes = "暂无描述")
    @GetMapping("/{id}")
    public Mono<Student> getOneById(@PathVariable("id") @ApiParam(value = "学生ID") Integer id) {
        return studentService.getById(id);
    }

    @GetMapping("/name/{merchantKey}/{name}")
    public Flux<Student> getStudentsByName(@PathVariable("merchantKey") String merchantKey, @PathVariable("name") String name) {
        return studentService.getStudentsByName(merchantKey, name);
    }

    @PostMapping("/")
    @ApiOperation(value = "测试控制器-测试方法，更新/插入学生数据", notes = "暂无描述")
    public Mono<Student> saveStudent(@RequestBody Student student) {
        log.info("要保存的学生信息：{}", student);
        Update update = Update.update("id", student.getId())
                .set("name", student.getName())
                .set("age", student.getAge());
        Query query = Query.query(Criteria.where("id").is(student.getId()));
        return template.update(Student.class).matching(query)
                .apply(update)
                .contextWrite(context -> context.put(MySqlDynamicDatasourceConfig.MERCHANT_KEY, "master"))
                .then(studentRepository.findById(student.getId()));
    }

    @PostMapping("/insert1")
    @ApiOperation(value = "测试控制器-测试方法，更新/插入学生数据", notes = "暂无描述")
    public Mono<Student> insertStudent1(@RequestBody Student student) {
        log.info("要保存的学生信息：{}", student);
        return template.insert(Student.class).into(SqlIdentifier.from(
                SqlIdentifier.unquoted("student")
        )).using(student);
    }

    @PostMapping("/insert2")
    @ApiOperation(value = "测试控制器-测试方法，更新/插入学生数据", notes = "暂无描述")
    public Mono<Student> insertStudent2(@RequestBody Student student) {
        log.info("要保存的学生信息：{}", student);
        return template.insert(Student.class).into("student").using(student);
    }

    @PostMapping("/insert3")
    @ApiOperation(value = "测试控制器-测试方法，更新/插入学生数据", notes = "暂无描述")
    public Mono<Student> insertStudent3(@RequestBody Student student) {
        log.info("要保存的学生信息：{}", student);
        return template.insert(student);
    }

    @PostMapping("/insert4")
    @ApiOperation(value = "测试控制器-测试方法，更新/插入学生数据", notes = "暂无描述")
    public Mono<Student> insertStudent4(@RequestBody Student student) {
        log.info("要保存的学生信息：{}", student);
        // databaseClient.sql()

        return saveEntity(student);
    }

    @Resource
    R2dbcEntityTemplateRepository repository;

    @PostMapping("/insert5")
    @ApiOperation(value = "测试控制器-测试方法，更新/插入学生数据", notes = "暂无描述")
    protected Mono<Integer> update(@RequestBody Student entity) {
        return repository.update(entity, Criteria.from(Criteria.where("id").is(entity.getId())));
    }

    private <T> Mono<T> saveEntity(T t) {
        Objects.requireNonNull(t);
        Class<?> aClass = t.getClass();
        if (aClass.isAnnotationPresent(Table.class)) {
            Table annotation = aClass.getAnnotation(Table.class);
            String tableName = annotation.value();
            String sql = "insert into " + tableName + " (#) values (#)";

            StringBuilder firstArg = new StringBuilder();
            Field[] declaredFields = aClass.getDeclaredFields();
            Set<String> fieldNameSet = new LinkedHashSet<>();
            for (Field declaredField : declaredFields) {
                if (declaredField.isAnnotationPresent(Column.class)) {
                    Column column = declaredField.getAnnotation(Column.class);
                    String columnName = column.value();
                    firstArg.append(columnName).append(",");
                    fieldNameSet.add(declaredField.getName());

                }
            }

            String firstParam = firstArg.delete(firstArg.length() - 1, firstArg.length()).toString();
            sql = sql.replaceFirst("#", firstParam);
            System.out.println(fieldNameSet);
            System.out.println(firstParam);
            System.out.println(sql);
        }
        return Mono.empty();
    }

    @PostMapping("/saves")
    public Flux<Student> saveStudentList(@RequestBody List<Student> studentList) {
        log.info("要保存的学生列表信息：{}", studentList);
        return studentService.saveStudent(studentList);
    }

    @DeleteMapping("/{id}")
    public Mono<Void> deleteStudent(@PathVariable("id") Integer id) {
        return studentService.deleteStudent(id);
    }

    @GetMapping("/select/{subName}/{currentPage}")
    public Flux<Student> selectStudent(@PathVariable("currentPage") Integer currentPage, @PathVariable("subName") String subName) {
        return studentService.selectStudent(currentPage, subName);
    }

    @PostMapping("/postSingle")
    public Mono<Student> postSingleData(@RequestBody Student student) {
        student.setName(student.getName() + Instant.now());
        return Mono.just(student);

    }

    /**
     * 设置 form 提交数据， POST 请求
     *
     * @param studentName 学生姓名
     * @param namePrefix  姓名前缀
     * @return 设置名称后的学生信息
     */
    @PostMapping(value = "/postForm", consumes = {MediaType.APPLICATION_FORM_URLENCODED_VALUE})
    public Mono<Student> postFormData(@RequestParam(name = "studentName") String studentName, @RequestParam(name = "namePrefix") String namePrefix) {
        Student student = new Student();
        student.setName(namePrefix + studentName);
        return Mono.just(student);
    }


    @PostMapping(value = "/postRequestBody/{age}")
    public Mono<Student> postRequestBody(@RequestBody Student student, @PathVariable Integer age) {
        student.setName(student.getName() + Instant.now().toEpochMilli());
        student.setAge(age);
        return Mono.just(student);
    }

    @PostMapping(value = "/postRequestBodyWithParam")
    public Mono<Student> postRequestBodyWithParam(@RequestBody Student student, @RequestParam("id") Integer id) {
        student.setName(student.getName() + Instant.now().toEpochMilli());
        student.setId(id);
        return Mono.just(student);
    }

    @PostMapping(value = "/formRequest1", consumes = {MediaType.APPLICATION_FORM_URLENCODED_VALUE})
    public Mono<Student> formRequest1(@RequestParam("name") String name, @RequestParam("id") Integer id) {
        Student student = new Student();
        student.setName(name + Instant.now().toEpochMilli());
        student.setId(id);
        return Mono.just(student);
    }


    @PostMapping(value = "/formRequest2", consumes = {MediaType.APPLICATION_FORM_URLENCODED_VALUE})
    public Mono<Student> formRequest2(@ModelAttribute("formPeople") FormPeople formPeople) {
        Student student = new Student();
        // 调试异常
        try {
            int i = 1 / 0;
        } catch (Exception e) {
            throw new RuntimeException("发生运行时异常" + e.getMessage());
        }

        student.setName(formPeople.getName() + Instant.now().toEpochMilli());
        student.setId(Integer.parseInt(formPeople.getId()));
        return Mono.just(student);
    }

    @PostMapping("/example1")
    @ApiOperation(value = "测试控制器-测试方法，通过样例查学生数据1", notes = "暂无描述")
    public Flux<Student> selectByExample1(Student student) {
        return studentService.selectStudentByExample1(student);
    }

    @PostMapping("/example2")
    @ApiOperation(value = "测试控制器-测试方法，通过样例查学生数据2", notes = "暂无描述")
    public Flux<Student> selectByExample2(Student student) {
        return studentService.selectStudentByExample2(student);
    }
}
